package com.web.startup;

import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Lists;
import com.web.filter.CustomInterceptor;
import com.web.filter.TraceInterceptor;
import com.web.validator.GlobalValidator;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.xml.MarshallingHttpMessageConverter;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.oxm.xstream.XStreamMarshaller;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.validation.Validator;
import org.springframework.web.multipart.support.StandardServletMultipartResolver;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.thymeleaf.spring4.SpringTemplateEngine;
import org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.spring4.view.ThymeleafViewResolver;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.util.*;


@Configuration
@EnableWebMvc
@ComponentScan(value = {"com.web", "com.core.model"})
@EnableScheduling
@EnableSwagger2
public class SpringMvcConfig extends WebMvcConfigurerAdapter {


    /**
     * 供 @Value 注解使用
     * 重要配置加密处理
     *
     * @return PropertyPlaceholderConfigurer
     */
    @Bean
    public PropertyPlaceholderConfigurer datasourceProperties() {
        PropertyEncrypt propertyEncrypt = new PropertyEncrypt();
        propertyEncrypt.setLocations(new ClassPathResource("database.properties"), new ClassPathResource("config.properties"));
        return propertyEncrypt;
    }

    @Bean
    public BeanPostProcessor persistenceException() {
        return new PersistenceExceptionTranslationPostProcessor();
    }

    /**
     * 配置jsp视图解析器
     * 此视图解析器作为解析器链的最后一个，无论是否找到视图都会返回
     *
     * @return InternalResourceViewResolver
     */
    @Bean
    public InternalResourceViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/views/");
        viewResolver.setSuffix(".jsp");
        return viewResolver;
    }

    /**
     * 配置静态资源路径映射
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
        registry.addResourceHandler("/static/**").addResourceLocations("/static/");
        registry.addResourceHandler("/public/**").addResourceLocations("/public/");
        registry.addResourceHandler("/favicon.ico").addResourceLocations("/static/favicon.ico");
    }

    /**
     * 添加自定义拦截器
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new TraceInterceptor()).addPathPatterns("/**");
//        registry.addInterceptor(new CustomInterceptor()).addPathPatterns("/**").excludePathPatterns("/refuse", "/aop/**");
    }

    /**
     * 配置国际化资源
     *
     * @return ResourceBundleMessageSource
     */
    @Bean
    public MessageSource messageSource() {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        messageSource.setDefaultEncoding("utf-8");
        messageSource.setBasenames("message");
        return messageSource;
    }

    /**
     * 配置 SpringMvc 基于 Servlet 3.0 的文件上传解析器
     * 配置延迟解析，以便统一处理异常
     *
     * @return StandardServletMultipartResolver
     */
    @Bean
    public StandardServletMultipartResolver multipartResolver() {
        StandardServletMultipartResolver resolver = new StandardServletMultipartResolver();
        resolver.setResolveLazily(true);
        return resolver;
    }

    /**
     * 配置全局验证器
     *
     * @return GlobalValidator
     */
    @Override
    public Validator getValidator() {
        return new GlobalValidator();
    }

    /**
     * 配置邮件发送实现类
     *
     * @return JavaMailSenderImpl
     */
    @Bean
    public JavaMailSender mailSender() {
        JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
        mailSender.setHost("smtp.qq.com");
        mailSender.setPort(465);
        mailSender.setProtocol("smtps");
        mailSender.setUsername("1411802214@qq.com");
        mailSender.setPassword("ufjtvvqsagjlbagc");
        mailSender.setDefaultEncoding("utf-8");
        Properties prop = new Properties();
        prop.put("mail.smtp.auth", true);
        prop.put("mail.smtp.ssl.enable", true);
        prop.put("mail.smtp.timeout", "25000");
        mailSender.setJavaMailProperties(prop);
        return mailSender;
    }

    /**
     * 配置邮件模版视图解析器
     *
     * @return SpringResourceTemplateResolver
     */
    @Bean
    public SpringResourceTemplateResolver emailTemplateResolver() {
        SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
        resolver.setPrefix("/WEB-INF/mail/");
        resolver.setSuffix(".html");
        resolver.setTemplateMode("HTML5");
        resolver.setCharacterEncoding("utf-8");
//        resolver.setOrder(1);
        return resolver;
    }

    /**
     * 依次配置 Thymeleaf 模版视图解析器
     *
     * @return ServletContextTemplateResolver
     */
    @Bean
    @Profile("thymeleaf")
    public ServletContextTemplateResolver webTemplateResolver() {   //模版解析器 1
        ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver();
        templateResolver.setPrefix("/WEB-INF/templates/");
        templateResolver.setSuffix(".html");
        templateResolver.setCacheable(false);
        templateResolver.setTemplateMode("HTML5");
        templateResolver.setCharacterEncoding("utf-8");
        return templateResolver;
    }

    @Bean
    @Profile("thymeleaf")
    public SpringTemplateEngine templateEngine() {  // 2
        SpringTemplateEngine engine = new SpringTemplateEngine();
        engine.addTemplateResolver(webTemplateResolver());
        return engine;
    }

    @Bean
    @Profile("thymeleaf")
    public ThymeleafViewResolver thymeleafViewResolver() {  // 3
        ThymeleafViewResolver thymeleafViewResolver = new ThymeleafViewResolver();
        thymeleafViewResolver.setTemplateEngine(templateEngine());
//        thymeleafViewResolver.setOrder(0);  //开启模版会导致jsp无法解析，暂未修复
        return thymeleafViewResolver;
    }

    /**
     * 使用swagger2构建RESTful APIs
     * 使用扫描包配置包下所有的api
     *
     * @return
     */
    @Bean
    public Docket createRestAPI() {
        return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select().apis(RequestHandlerSelectors.basePackage("com.web.controller")).paths(PathSelectors.any()).build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder().title("Spring Boot使用Swagger2构建RESTful APIs").description("更多精彩,欢迎关注").termsOfServiceUrl("http://www.baidu.com").contact(new Contact("程序猿XF", "http://www.baidu.com", "418791536@qq.com")).version("1.0").build();
    }

    /**
     * 配置内容协商类型
     * 开启url后缀匹配
     * 关闭参数匹配规则
     * 关闭请求头匹配规则
     * 默认：json
     *
     * @param configurer
     */
    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer.favorPathExtension(true)
                .favorParameter(false)
                .parameterName("format")
                .ignoreAcceptHeader(true)
                .useJaf(false)
                .defaultContentType(MediaType.APPLICATION_JSON_UTF8)
                .mediaType("json", MediaType.APPLICATION_JSON_UTF8)
                .mediaType("xml", MediaType.APPLICATION_XML)
                .mediaType("html", MediaType.TEXT_HTML);
    }

    /**
     * 增加xml内容转换器
     *
     * @param converters
     */
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(0, createFastJsonHttpMessageConverter());
        converters.add(createXmlHttpMessageConverter());
        super.configureMessageConverters(converters);
    }

    /**
     * 配置具体的xml转换器
     * <p/>依赖于 {@link com.thoughtworks.xstream.XStream}
     *
     * @return
     */
    private HttpMessageConverter createXmlHttpMessageConverter() {
        MarshallingHttpMessageConverter converter = new MarshallingHttpMessageConverter();
        XStreamMarshaller marshaller = new XStreamMarshaller();
        marshaller.setAutodetectAnnotations(true);
        converter.setMarshaller(marshaller);
        converter.setUnmarshaller(marshaller);
        return converter;
    }

    /**
     * 配置具体的json转换器
     * 用FastJson替换默认的Jackson2
     *
     * @return
     */
    private HttpMessageConverter createFastJsonHttpMessageConverter() {
        FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
        ArrayList<MediaType> mediaTypes = Lists.newArrayList(MediaType.APPLICATION_JSON_UTF8, MediaType.TEXT_HTML);
        converter.setSupportedMediaTypes(mediaTypes);
        FastJsonConfig config = new FastJsonConfig();
        config.setSerializerFeatures(
                SerializerFeature.WriteMapNullValue,
                SerializerFeature.QuoteFieldNames,
                SerializerFeature.WriteDateUseDateFormat,
                SerializerFeature.WriteNullListAsEmpty,
                SerializerFeature.WriteNullNumberAsZero,
                SerializerFeature.WriteNullStringAsEmpty,
                SerializerFeature.DisableCircularReferenceDetect
        );
        converter.setFastJsonConfig(config);
        return converter;
    }

}
